package com.znf4.id.utils;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

/**
 * TODO ... 无注释
 */
public class IdGeneratorUtil {

	static final Logger logger = LoggerFactory.getLogger(IdGeneratorUtil.class);
	/**
	 * JedisPool, luaSha
	 */
	List<Pair<JedisPool, String>> jedisPoolList;
	int retryTimes;

	int index = 0;

	private IdGeneratorUtil() {

	}

	private IdGeneratorUtil(List<Pair<JedisPool, String>> jedisPoolList, int retryTimes) {
		this.jedisPoolList = jedisPoolList;
		this.retryTimes = retryTimes;
	}

	static public IdGeneratorBuilder builder() {
		return new IdGeneratorBuilder();
	}

	public static class IdGeneratorBuilder {
		List<Pair<JedisPool, String>> jedisPoolList = new ArrayList<>();
		int retryTimes = 5;

		public IdGeneratorBuilder addHost(String host, int port, String password, String luaSha) {
			
			/** 获取redis 3***/
			jedisPoolList.add(Pair.of(new JedisPool(new GenericObjectPoolConfig(), host, port, 3000, password), luaSha));
			
			return this;
		}

		public IdGeneratorBuilder retryTimes(int retryTimes) {
			this.retryTimes = retryTimes;
			return this;
		}

		public IdGeneratorUtil build() {
			return new IdGeneratorUtil(jedisPoolList, retryTimes);
		}
	}

	public long next(String tab) {
		return next(tab, 0);
	}

	public long next(String tab, long shardId) {
		for (int i = 0; i < retryTimes; ++i) {
			Long id = innerNext(tab, shardId);
			if (id != null) {
				return id;
			}
		}
		throw new RuntimeException("Can not generate id!");
	}

	@SuppressWarnings("deprecation")
	Long innerNext(String tab, long shardId) {
		index++;
		Pair<JedisPool, String> pair = jedisPoolList.get(index % jedisPoolList.size());
		JedisPool jedisPool = pair.getLeft();

		String luaSha = pair.getRight();
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			@SuppressWarnings("unchecked")
			List<Long> result = (List<Long>) jedis.evalsha(luaSha, 2, tab, "" + shardId);
			long id = buildId(result.get(0), result.get(1), result.get(2), result.get(3));
			return id;
		} catch (Exception e) {
			if (jedis != null) {
				jedisPool.returnBrokenResource(jedis);
			}
			logger.error("generate id error!", e);
		} finally {
			if (jedis != null) {
				jedisPool.returnResource(jedis);
			}
		}
		return null;
	}

	public static long buildId(long second, long microSecond, long shardId, long seq) {
		long miliSecond = (second * 1000 + microSecond / 1000);
		return (miliSecond << (12 + 10)) + (shardId << 10) + seq;
	}

	public static List<Long> parseId(long id) {
		long miliSecond = id >>> 22;
		// 2 ^ 12 = 0xFFF
		long shardId = (id & (0xFFF << 10)) >> 10;
		long seq = id & 0x3FF;

		List<Long> re = new ArrayList<Long>(4);
		re.add(miliSecond);
		re.add(shardId);
		re.add(seq);
		return re;
	}

	/**
	 * 获取redis 客户端连接
	 */
	public Jedis getJedis() {
		
		Pair<JedisPool, String> pair = jedisPoolList.get(index % jedisPoolList.size());
		JedisPool jedisPool = pair.getLeft();
		Jedis jedis = jedisPool.getResource();
		
		return jedis;
	}

	/**
	 * 释放redis资源
	 * 
	 * @param jedis
	 */
	private void releaseResource(Jedis jedis) {
		if (jedis != null) {
			jedis.close();
			// jedisSentinelPool.destroy();
			// jedisSentinelPool.returnResource(jedis);
		}
	}

	/**
	 * 添加一个内容到指定key的String中
	 * 
	 * @param key
	 * @param field
	 * @param value
	 * @return
	 */
	public Boolean addString(String key,String value) {
		Jedis jedis = null;
		try {
			jedis = getJedis();
			jedis.set(key,value);
			return true;
		} catch (Exception e) {
			logger.error("Cache保存失败：", e);
			return false;
		} finally {
			releaseResource(jedis);
		}
	}

	/**
	 * 从指定String中拿一个对象
	 * 
	 * @param key
	 * @param field
	 * @return
	 */
	public Object getString(String key) {
		Jedis jedis = null;
		try {
			jedis = getJedis();
			String obj = jedis.get(key);
			return obj;
		} catch (Exception e) {
			logger.error("Cache读取失败：", e);
			return null;
		} finally {
			releaseResource(jedis);
		}
	}

	/**
	 * 从String中删除指定filed的值
	 * 
	 * @param key
	 * @param field
	 * @return
	 */
	public Boolean delString(String key) {
		Jedis jedis = null;
		try {
			jedis = getJedis();
			long result = jedis.del(key);
			return result == 1 ? true : false;
		} catch (Exception e) {
			logger.error("Cache删除失败：", e);
			return null;
		} finally {
			releaseResource(jedis);
		}
	}

	/**
	 * 验证该值，在String中是否存在。
	 * 
	 * @param key
	 * @param field
	 * @return
	 */
	public Boolean existsString(String key) {
		Jedis jedis = null;
		try {
			jedis = getJedis();
			Boolean result = jedis.exists(key);
			return result;
		} catch (Exception e) {
			logger.error("Cache验证String 是否存在该值失败：", e);
			return null;
		} finally {
			releaseResource(jedis);
		}
	}
	
	/**
	 * 获取Incr
	 * 
	 * @param key
	 * @param field
	 * @return
	 */
	public String getIncr(String key) {
		Jedis jedis = null;
		try {
			jedis = getJedis();
			
			System.out.println(jedis.get(key));
			
			String result = String.valueOf(jedis.incr(key));
			return result;
		} catch (Exception e) {
			logger.error("自增1失败 是否存在该值失败：", e);
			return null;
		} finally {
			releaseResource(jedis);
		}
	}
	
	
	
	
	
	

}
